package com.zkn.component.office.excel.handler;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.lang3.StringUtils;

import com.zkn.component.office.excel.annotation.ExcelField;
import com.zkn.component.office.excel.annotation.ExcelSheet;

/**
 * ExcelField Annotation Handler
 * @author frank.su
 * @date 2017/08/01
 *
 */
public class ExcelFieldHandler {
	
	public static <T> List<T> handle(List<Map<String,Object>> dataList, Class<T> clazz) throws Exception{
		List<T> list = new ArrayList<T>();
		
		if(!dataList.isEmpty()){
			Map<String, Field> fieldMap = parseClass(clazz);
			for(Map<String,Object> dataMap : dataList){
				list.add(parseObject(clazz, dataMap, fieldMap));
			}
		}
		
		return list;
	}
	
	public static <T> Map<String,Object> handle(T obj) throws Exception{
		Map<String,Object> map = new LinkedHashMap<String,Object>();
		
		Map<String, Field> fieldMap = parseClass(obj.getClass());
		for(Entry<String, Field> ef : fieldMap.entrySet()){
			ef.getValue().setAccessible(true);
			map.put(ef.getKey(), ef.getValue().get(obj));
		}
		
		return map;
	}
	
	public static <T> Map<String, Field> parseClass(Class<T> clazz){
		Map<String, Field> map = new LinkedHashMap<String, Field>();
		Field[] fields = clazz.getDeclaredFields();
		
		sort(fields);
		
		for (Field field : fields) {
			ExcelField excelField = field.getAnnotation(ExcelField.class);
			if(excelField != null){
				if(StringUtils.isNotBlank(excelField.title())){
					map.put(excelField.title(), field);
				} else if(StringUtils.isNotBlank(excelField.value())){
					map.put(excelField.value(), field);
				} else {
					map.put(field.getName(), field);
				}
			}
		}
		
		return map;
	}
	
	private static <T> T parseObject(Class<T> clazz, Map<String, Object> data, Map<String, Field> fieldMap) throws Exception{
		T obj = clazz.newInstance();
		for(Entry<String, Field> ef : fieldMap.entrySet()){
			ef.getValue().setAccessible(true);
			ef.getValue().set(obj, data.get(ef.getKey()));
		}
		
		return obj;
	}
	
	public static String parseSheetName(Class<?> clazz) throws Exception{
		ExcelSheet excelSheet = clazz.getAnnotation(ExcelSheet.class);
		if(excelSheet != null){
			return excelSheet.value();
		}
		
		return "";
	}
	
	private static void sort(Field[] fields){
		Arrays.sort(fields, new Comparator<Field>(){

			@Override
			public int compare(Field field1, Field field2) {
				ExcelField excelField1 = field1.getAnnotation(ExcelField.class);
				ExcelField excelField2 = field2.getAnnotation(ExcelField.class);
				
				if(excelField1 != null && excelField2 == null){
					return 1;
				} else if(excelField1 != null && excelField2 != null){
					return excelField1.order() - excelField2.order();
				}
				return 0;
			}
			
		});
	}
}
